package com.qf.leadnewsarticle.web.v1;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.qf.leadnewsarticle.mapper.ApArticleContentMapper;
import com.qf.leadnewsarticle.service.ArticleService;
import com.qf.qfleadnewscommons.minio.MinioOperator;
import com.qf.qfleadnewscommons.mvc.ThreadLocalUtils;
import com.qf.qfleadnewsmodel.article.dtos.ArticleHomePageDto;
import com.qf.qfleadnewsmodel.article.pojos.ApArticle;
import com.qf.qfleadnewsmodel.article.pojos.ApArticleContent;
import com.qf.qfleadnewsmodel.behavior.dtos.BehaviorDto;
import com.qf.qfleadnewsmodel.commons.consts.RedisConst;
import com.qf.qfleadnewsmodel.commons.dtos.ResponseResult;
import com.qf.qfleadnewsmodel.enums.AppHttpCodeEnum;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/v1")
@Slf4j
public class ArticleController {

    @Autowired
    private ArticleService articleService;

    @Autowired
    private MinioOperator minioOperator;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @PostMapping("/article/load")
    public ResponseResult load(@RequestBody ArticleHomePageDto articleHomePageDto){
        Long uid = ThreadLocalUtils.getUid();

        //log.info("-----------uid为：{}------------",uid);
        String tag = articleHomePageDto.getTag();
        //查询缓存
        String cacheKey = RedisConst.ARTICLE_HOT_CACHE_PREFIX + tag;

        String cacheStr = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isNotBlank(cacheStr)){
            List<ApArticle> apArticles = JSON.parseArray(cacheStr, ApArticle.class);

            if (apArticles != null && apArticles.size() > 0){
                log.info("----命中缓存----tag:{}",tag);

                return ResponseResult.okResult(apArticles);
            }
        }

        log.info("----没有命中缓存----tag:{}",tag);
        ResponseResult responseResult = articleService.loadPage(articleHomePageDto);
        cacheStr = JSON.toJSONString(responseResult.getData());
        redisTemplate.opsForValue().set(cacheKey,cacheStr);

        return responseResult;
    }

    @PostMapping("/article/loadnew")
    public ResponseResult loadnew(@RequestBody ArticleHomePageDto articleHomePageDto){

        return articleService.loadPage(articleHomePageDto);
    }

    @PostMapping("/article/loadmore")
    public ResponseResult loadmore(@RequestBody ArticleHomePageDto articleHomePageDto){

        return articleService.loadPage(articleHomePageDto);
    }

    @PostMapping("/collection_behavior")
    public ResponseResult collection(@RequestBody BehaviorDto behaviorDto){
        //当前用户是否对该文章进行过收藏
        Integer operation = behaviorDto.getOperation();
        String key = RedisConst.ARTICLE_COLLECTION_PREFIX + behaviorDto.getEntryId();
        //利用当前点赞用户的id作为文章位图的偏移量
        long offset = ThreadLocalUtils.getUid();
        boolean value = false;
        if(operation == 0){
            //0-点赞  setBit
            value = true;
        }else{
            //1-取消点赞 setBit
            value = false;
        }
        redisTemplate.opsForValue().setBit(key,offset,value);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS.getCode(),AppHttpCodeEnum.SUCCESS.getErrorMessage());
    }

    @PostMapping("/article/load_article_behavior")
    public ResponseResult loadBehavior(@RequestBody BehaviorDto behaviorDto){
        //用户是否对当前文章进行过点赞、不喜欢、收藏过行为
        String articleId = behaviorDto.getArticleId();
        long offset = ThreadLocalUtils.getUid();
        String likeKey = RedisConst.ARTICLE_LIKE_PREFIX + articleId;
        Boolean likeBool = redisTemplate.opsForValue().getBit(likeKey, offset);

        String unlikeKey = RedisConst.ARTICLE_UNLIKE_PREFIX + articleId;
        Boolean unlikeBool = redisTemplate.opsForValue().getBit(unlikeKey, offset);

        String collectionKey = RedisConst.ARTICLE_COLLECTION_PREFIX + articleId;
        Boolean collectionBool = redisTemplate.opsForValue().getBit(collectionKey, offset);

        Map map = new HashMap();
        map.put("islike",likeBool);
        map.put("isunlike",unlikeBool);
        map.put("iscollection",collectionBool);

        return ResponseResult.okResult(map);
    }

    @GetMapping("/test")
    public String test(HttpServletRequest request) throws IOException {

        InputStream is = new FileInputStream("C:\\Users\\aming\\Desktop\\Redisson加锁源码分析.jpg");

        String url = minioOperator.upload(is, "test.jpg",request);

        is.close();

        return url;
    }

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private Configuration configuration;

    @GetMapping("/html")
    public String processStaticArticleHtml(HttpServletRequest request) throws IOException, TemplateException {

        Long articleId = 1647045893685268482l;

        //根据id查找内容
        LambdaQueryWrapper<ApArticleContent> qw = Wrappers.lambdaQuery(ApArticleContent.class)
                                            .eq(ApArticleContent::getArticleId, articleId);
        ApArticleContent apArticleContent = apArticleContentMapper.selectOne(qw);

        String content = apArticleContent.getContent();

        //将字符串类型的content转成数组/集合
        JSONArray jsonArray = JSONArray.parseArray(content);

        //数据模型
        Map map = new HashMap();
        map.put("content",jsonArray);

        Template template = configuration.getTemplate("article.ftl");

        /**
         * 这里我们将内容都直接在内存中处理，有一个风险。
         *      如果文章的内容过大，并发量很高，内存的开销就会剧增，最终结果可能会导致OOM问题
         *
         *      解决方案：基于临时文件操作
         *          StringWriter --> FileOutputStream
         *          ByteArrayInputStream --> FileInputStream
         *
         *  EasyExcel： 基于PIO做了进一步封装，解决了OOM问题
         *      PIO原始操作所有的数据操作都是在内存中完成的
         *      EasyExcel将内存操作换成本地临时文件操作
         */
        StringWriter os = new StringWriter();
        template.process(map,os);

        //将生成的html内容，上传到minio中
        InputStream is = new ByteArrayInputStream(os.toString().getBytes(StandardCharsets.UTF_8));
        String url = minioOperator.upload(is, "test.html",request);

        //将url填充到article表中
        ApArticle article = articleService.getById(articleId);
        article.setStaticUrl(url);
        articleService.updateById(article);

        return url;
    }
}
